﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows.Threading;

namespace Milkify.Input
{
    public class DecoupledCommand<T> : ICommand
    {
        private readonly Action<T> mExecuteCallback;
        private readonly Func<T, bool> mCanExecuteCallback;
        private readonly Dispatcher mDispatcher;

        public DecoupledCommand(Action<T> executeCallback)
            : this(executeCallback, null)
        {
        }

        public DecoupledCommand(Action<T> executeCallback, Func<T, bool> canExecuteCallback)
        {
            if (executeCallback == null)
                throw new ArgumentNullException("executeCallback");

            mExecuteCallback = executeCallback;
            mCanExecuteCallback = canExecuteCallback;
            mDispatcher = App.Current.Dispatcher;
        }

        public bool CanExecute(T parameter)
        {
            if (mCanExecuteCallback == null)
                return true;
            return mCanExecuteCallback(parameter);
        }

        public void Execute(T parameter)
        {
            if (CanExecute(parameter))
            {
                if (mExecuteCallback != null)
                    mExecuteCallback(parameter);
            }
        }

        public void InvalidateCanExecute()
        {
            OnCanExecuteChanged(EventArgs.Empty);
        }

        protected virtual void OnCanExecuteChanged(EventArgs e)
        {
            if (CanExecuteChanged != null)
            {
                if (mDispatcher.CheckAccess())
                {
                    CanExecuteChanged(this, e);
                }
                else
                {
                    mDispatcher.BeginInvoke(DispatcherPriority.Normal, (Action<EventArgs>)OnCanExecuteChanged, e);
                }
            }
        }

        #region ICommand Members

        public event EventHandler CanExecuteChanged;

        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute((T)parameter);
        }

        void ICommand.Execute(object parameter)
        {
            Execute((T)parameter);
        }

        #endregion
    }
}
